package com.wy.timer.impl;
import java.util.concurrent.PriorityBlockingQueue;

/**
 * @author 美女
 * @date 2022/05/02 22:59
 **/
public class MyTimer {
    //定义出来队列，这里定义的是普通属性不是静态属性，意味着静态内部类中看不到queue,所以把底下静态内部类Worker的属性改为普通即可
    //优先级队列意味元素与元素间具备可比较能力，而MyTimerTask并不具备，需要实现接口让其具备课比较能力
    private final PriorityBlockingQueue<MyTimerTask> queue=new PriorityBlockingQueue<>();

    private final Object newTaskComming=new Object();
    public MyTimer(){
        Worker worker=new Worker();
        worker.start();//启动子线程
    }

    //工作线程-取任务，执行任务
    //由工作线程运行任务
    //不能使用静态内部类，否则看不到外部类的属性
     class Worker extends Thread{
        @Override
        public void run() {
            while (true){//工作线程不断地在循环
                //队列-从队列中取任务出来
                MyTimerTask task= null;
                try {
                    task = queue.take();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //task应该有个执行的时刻（不能简单记录delay,应该纪录时刻）
                long now=System.currentTimeMillis();//当前执行时刻
                long delay=task.runAt-now;//应该延迟的时间（应何时运行）-当前时间
                if(delay<=0){//立即执行
                    task.run();//执行这个任务
                }else{
                    //休眠一段时间再去执行任务
                    try {
//                        Thread.sleep(delay);
                        //应该有两种情况醒来
                        //1.有新的任务进来（任务可能比当前最小的任务更靠前）
                        //2.没有新的任务进来，到了该执行现在的任务的时候了
                        synchronized (newTaskComming){
                            newTaskComming.wait(delay);//最多等delay这么久
                        }
                        //如果当前时间已经在要执行任务的时间之后了
                        //说明任务的执行时间已过，该去执行任务了
                        //否则，先把这个当前任务放回队列（因为时间还未到，再去取最小的任务）
                        if(System.currentTimeMillis()>=task.runAt){//当前时间>=执行任务时间
                            task.run();
                        }else{
                            //否则这个任务暂时不应该执行了，放回队列中，取下一个任务进来
                            queue.put(task);
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }//保证从队列中取出来的永远是离线程最近的一个任务

    public void schedule(MyTimerTask task,long delay){
        //该方法由非工作线程调用(这里由主线程调用，主线程设置的闹钟)
        task.runAt=System.currentTimeMillis()+delay;//什么时候该执行：当前时间+delay

        //把这个任务加到优先级阻塞队列中
        queue.put(task);
        synchronized (newTaskComming){
            newTaskComming.notify();//尝试唤醒
        }
    }
}
